package com.infinitus.activity.web;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.infinitus.activity.actenum.QualiStateEnum;
import com.infinitus.activity.common.ResponseCodeConstant;
import com.infinitus.activity.config.ActivityConfig;
import com.infinitus.activity.pojo.DO.AuldrawVo;
import com.infinitus.activity.pojo.VO.ActivityConfigVO;
import com.infinitus.activity.pojo.VO.AwardsConfigVO;
import com.infinitus.activity.pojo.VO.QualificationVo;
import com.infinitus.activity.pojo.VO.ResponseVO;
import com.infinitus.activity.service.AuldrawWinnerService;
import com.infinitus.activity.service.ConfigService;
import com.infinitus.activity.service.DistributionLockService;
import com.infinitus.activity.util.*;
import org.apache.poi.poifs.crypt.EcmaDecryptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import redis.clients.jedis.Jedis;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.security.Key;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;

/**
 * @create: 2019/1/9
 * @desc:
 */
@RestController
@RequestMapping("/lottery")
public class LotteryController {

    private Logger logger = LoggerFactory.getLogger(LotteryController.class);

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    DistributionLockService distributionLockService;

    @Autowired
    AuldrawWinnerService auldrawWinnerService;

    @Autowired
    ConfigService configService;

    @Autowired
    private ActivityConfig activityConfig;

    private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Value("${activity-code}")
    private String activityCode;    //活动编码,唯一

    @Value("${deploycolId}")
    private String deploycolId;     //活动部署FkId

    @Value("${spring.redis.host}")
    private String redisHost;

    @Value("${spring.redis.port}")
    private String redisPort;

    /**
     * 对外抽奖接口
     *
     * @param dealerNo
     * @return
     */
    @RequestMapping(path = "/act", method = RequestMethod.GET)
    public ResponseEntity<?> lottery(HttpServletResponse response , @RequestParam String dealerNo) throws Exception {
        //卡号格式合法性校验
        if (!Pattern.matches("^\\d{9}$", dealerNo) || StringUtils.isEmpty(dealerNo)) {
            return ResponseEntity.badRequest().body(ResponseCodeConstant.ILLEGAL_DEALERNO);
        }
        //改成从初始化就有活动配置的activityConfigVO中拿
        ActivityConfigVO activityConfigVO = activityConfig.activityConfigVO();
        String activityTime = activityConfigVO.getActivityTime();
        String lotteryTime = activityConfigVO.getLotteryTime();

        String[] lotteryTimeArray = lotteryTime.split(",");
        activityConfigVO.setLotteryStartDate(lotteryTimeArray[0]);
        activityConfigVO.setLotteryEndDate(lotteryTimeArray[1]);

        // 活动开始结束时间
        Date lotteryStartDate = DateUtil.parse2yyyyMMddHHmmss(activityConfigVO.getLotteryStartDate());
        Date lotteryEndDate = DateUtil.parse2yyyyMMddHHmmss(activityConfigVO.getLotteryEndDate());
        //判断活动是否开始
        if(System.currentTimeMillis() < lotteryStartDate.getTime()) {
            return ResponseEntity.ok(new ResponseVO<>().setCode(ResponseCodeConstant.ACTIVITY_UNSTART));
        }
        //判断活动是否已结束
        if(System.currentTimeMillis() > lotteryEndDate.getTime()){
            return ResponseEntity.ok(new ResponseVO<>().setCode(ResponseCodeConstant.ACTIVITY_ENDED));
        }
        // 获取登录标识
        Boolean hasKey = redisTemplate.hasKey(KeyGenerator.getLoginAlready(deploycolId,dealerNo));
        // 是否已登录
        if (!hasKey) {
            return ResponseEntity.ok(new ResponseVO<>().setCode(ResponseCodeConstant.UNLOGIN));
        }
        // redis锁的唯一标识
        String clientId = UUID.randomUUID().toString();
        try {
            // 获取redis锁
            if (!tryLock(activityCode, clientId, 5)) {
                return ResponseEntity.ok(new ResponseVO<>().setContent("系统繁忙，请稍后再试!"));
            }
            // 从redis获取该活动的活动配置
            // 添加活动抽奖总次数标记
            String lotteryCounts =
                    stringRedisTemplate.opsForValue().get(KeyGenerator.getActivityLotteryCounts(deploycolId));
            if (null == lotteryCounts) {
                stringRedisTemplate.opsForValue().set(KeyGenerator.getActivityLotteryCounts(deploycolId), "0");
            }
            String ruleType = activityConfigVO.getRuleType();
            //如果是无需抽奖资格，只允许抽一次
            if(ruleType.equals("0")) {
                String lotteryAlready = stringRedisTemplate.opsForValue().get(KeyGenerator.getAuldrawLotteryAlready(deploycolId,dealerNo));
                // 如果该key不存在，则说明该用户未抽过奖
                if (null == lotteryAlready) {
                    stringRedisTemplate.opsForValue().set(KeyGenerator.getAuldrawLotteryAlready(deploycolId,dealerNo), "0");
                    AwardsConfigVO awardsConfigVO = lotteryOperation(deploycolId); //抽奖
                    //活动抽奖总次数加1
                    redisTemplate.opsForValue().increment(KeyGenerator.getActivityLotteryCounts(deploycolId), 1);
                    //存入Redis
                    //生成资格序列号
                    String qualiSeqNo = CodeUtils.getUUID();
                    AuldrawVo auldrawVo = new AuldrawVo();
                    auldrawVo.setDealerNo(dealerNo);                          //卡号
                    auldrawVo.setGiftCode(awardsConfigVO.getAwardsCode());    //中奖奖项Id
                    auldrawVo.setAwardsName(awardsConfigVO.getAwardsName());  //奖品名称
                    auldrawVo.setActivityCode(activityCode);                  //活动编码
                    auldrawVo.setGiftQty(awardsConfigVO.getGiftQty());        //奖品数量 前端没配就null
                    auldrawVo.setCreateTime(new Date());
                    auldrawVo.setQualiSeqNo(qualiSeqNo);                      //序列号
                    //8080是没有中奖
                    if(awardsConfigVO.getAwardsCode().equals("8080")) {
                        auldrawVo.setWinningPrize(0);
                    }else {
                        auldrawVo.setWinningPrize(1);
                    }
                    Jedis jedis = null;
                    try {
                        jedis = new Jedis(redisHost, Integer.valueOf(redisPort));
                        jedis.connect();
                        String jsonString = JSON.toJSONString(auldrawVo);
                        //保存中奖纪录(用于作历史记录查看)
                        jedis.hset(KeyGenerator.getActivityListWinningBackup(deploycolId,dealerNo),qualiSeqNo,jsonString);

                        //用于同步
                        jedis.hset(KeyGenerator.getActivityListWinningSynch(deploycolId,dealerNo),qualiSeqNo,jsonString);
                        awardsConfigVO.setQualiSeqNo(qualiSeqNo);
                    }catch (Exception e) {
                        e.printStackTrace();
                    }finally{
                        if(jedis != null) {
                            jedis.close();
                        }
                    }

                    //保存中奖纪录后，如果是未中奖，则返回未中奖提示
                    if(awardsConfigVO.getAwardsCode().equals("8080")) {
                        return ResponseEntity.ok(new ResponseVO<>().setCode(ResponseCodeConstant.LOTTERY_BUT_FAIL)); //未中奖
                    }
                    return ResponseEntity.ok(new ResponseVO<>().setContent(awardsConfigVO));
                } else {
                    return ResponseEntity.ok(new ResponseVO<>().setCode(ResponseCodeConstant.LOTTERY_TIMES_END)); //已抽奖
                }
            }else {
                //资格抽奖
                Map<String, QualificationVo> map = getQualificationVo(deploycolId,dealerNo);
                if(map != null && map.size() > 0) {
                    for (Map.Entry<String, QualificationVo> entry : map.entrySet()) {
                        QualificationVo qualificationVo = entry.getValue();
                        //如果vo的资格状态为可用才能抽奖,并且抽完奖要设置为已用
                        if(qualificationVo.getQualiStatus() == QualiStateEnum.AVAILABLE.getState() || String.valueOf(qualificationVo.getQualiStatus()).equals("1")) {
                            // 进行抽奖
                            AwardsConfigVO awardsConfigVO = lotteryOperation(deploycolId);
                            // 活动抽奖总次数加一
                            redisTemplate.opsForValue().increment(KeyGenerator.getActivityLotteryCounts(deploycolId),1);
                            //资格抽奖用户的已抽奖次数
                            redisTemplate.opsForValue().increment(KeyGenerator.getActivityQualiLotteryCounts(deploycolId,dealerNo),1);
                            // 中奖记录
                            Jedis jedis = null;
                            try {
                                AuldrawVo auldrawVo = new AuldrawVo();
                                auldrawVo.setDealerNo(dealerNo);                          //卡号
                                auldrawVo.setGiftCode(awardsConfigVO.getAwardsCode());    //中奖奖项Id
                                auldrawVo.setAwardsName(awardsConfigVO.getAwardsName());  //奖品名称
                                auldrawVo.setActivityCode(activityCode);                  //活动编码
                                auldrawVo.setCreateTime(new Date());
                                auldrawVo.setGiftQty(awardsConfigVO.getGiftQty());        //奖品数量
                                auldrawVo.setQualiSeqNo(qualificationVo.getQualiSeqNo()); //序列号
                                //8080是没有中奖
                                if(awardsConfigVO.getAwardsCode().equals("8080")) {
                                    auldrawVo.setWinningPrize(0);
                                }else {
                                    auldrawVo.setWinningPrize(1);
                                }
                                jedis = new Jedis(redisHost, Integer.valueOf(redisPort));
                                jedis.connect();
                                String jsonString = JSON.toJSONString(auldrawVo);
                                //保存中奖纪录
                                jedis.hset(KeyGenerator.getActivityListWinningBackup(deploycolId,dealerNo),qualificationVo.getQualiSeqNo(),jsonString);
                                //用于同步
                                jedis.hset(KeyGenerator.getActivityListWinningSynch(deploycolId,dealerNo),qualificationVo.getQualiSeqNo(),jsonString);
                                awardsConfigVO.setQualiSeqNo(qualificationVo.getQualiSeqNo());
                                //资格设置为已用
                                qualificationVo.setQualiStatus(0);
                                jedis.hset(deploycolId+":"+dealerNo,qualificationVo.getQualiSeqNo(),JSON.toJSONString(qualificationVo));
                            }catch (Exception e) {
                                e.printStackTrace();
                            }finally {
                                if(jedis != null){
                                    jedis.close();
                                }
                            }
                            //保存中奖纪录后，如果是没有奖品数量，则返回未中奖提示
                            if(awardsConfigVO.getAwardsCode().equals("8080")) {
                                return ResponseEntity.ok(new ResponseVO<>().setCode(ResponseCodeConstant.LOTTERY_BUT_FAIL)); //未中奖
                            }
                            return ResponseEntity.ok(new ResponseVO<>().setContent(awardsConfigVO));
                        }
                    }
                }
                // 没有抽奖资格了
                return ResponseEntity.ok(new ResponseVO<>().setCode(ResponseCodeConstant.NO_LOTTERY_QUA));
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("", e);
            return null;
        } finally {
            distributionLockService.unlock(activityCode, clientId);
        }
    }

    private AwardsConfigVO lotteryOperation(String deploycolId) {
        Jedis jedis = null;
        AwardsConfigVO awardsConfigVO=null;
        try {
            // 奖品code
            List<String> awardsCodeList = new ArrayList<>();
            // 单个奖品中奖概率
            List<Double> awardsRateList = new ArrayList<>();
            // 从redis中获取活动对应的奖品
            Map<String, AwardsConfigVO> awardsConfigVOMap = getAwardsList(deploycolId);
            for (Map.Entry<String, AwardsConfigVO> entry : awardsConfigVOMap.entrySet()) {
                awardsCodeList.add(entry.getKey());
                awardsRateList.add(entry.getValue().getAwardsRate().doubleValue());
            }
            // 该抽奖了
            int awardsIndex = AwardsUtils.drawGift(awardsRateList);
            String awardsCode = awardsCodeList.get(awardsIndex);
            // 奖品数量不为0，则返回抽中奖品vo
            awardsConfigVO = awardsConfigVOMap.get(awardsCode);
            // 如果奖品数量为0
            jedis = new Jedis(redisHost, Integer.valueOf(redisPort));
            jedis.connect();
            String alreadyCount = jedis.hget(KeyGenerator.getAwardsAlreadyCount(deploycolId), awardsCode);
            if (awardsConfigVO.getAwardsCounts() - Integer.parseInt(alreadyCount) <= 0) {
                awardsConfigVO.setAwardsCode("8080");
                awardsConfigVO.setAwardsName(null);
                return awardsConfigVO;
            }
           jedis.hincrBy(KeyGenerator.getAwardsAlreadyCount(deploycolId),awardsCode,1);
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(jedis != null) {
                jedis.close();
            }
        }
        return awardsConfigVO;
    }

    private Map<String, AwardsConfigVO> getAwardsList(String deploycolId) {

        Map<String, AwardsConfigVO> maps = new HashMap<>();

        String activityConfigJSON
                = stringRedisTemplate.opsForValue().get(KeyGenerator.getAwardsConfig(deploycolId));
        List<AwardsConfigVO> list = JSONArray.parseArray(activityConfigJSON, AwardsConfigVO.class);

        for (int i = 0; i < list.size(); i++) {
            AwardsConfigVO awardsConfigVO = list.get(i);
            maps.put(awardsConfigVO.getAwardsCode(), awardsConfigVO);
        }
        return maps;
    }

    /**
     * @Desc 从Redis中获取资格
     */
    private Map<String,QualificationVo> getQualificationVo(String deploycolId , String dealerNo) {
        Jedis jedis = null;
        Map<String,QualificationVo> voMap = new HashMap<>();
        try {
            jedis = new Jedis(redisHost , Integer.valueOf(redisPort));
            jedis.connect();
            Map<String, String> map = jedis.hgetAll(deploycolId+":"+dealerNo);  //資格->   活動部署Id:卡号
            for (Map.Entry<String, String> entry : map.entrySet()) {
                QualificationVo qualificationVo = JSONObject.parseObject(entry.getValue(), QualificationVo.class);
                voMap.put(entry.getKey(),qualificationVo);
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        return voMap;
    }

    /**
     * 尝试获取redis锁
     * 如果仍没有获取到锁，则返回当前结果
     *
     * @param activityCode redis锁的名称
     * @param clientId     锁的唯一标志
     * @param retryTimes   获取锁失败时重试次数
     * @return
     */
    private boolean tryLock(String activityCode, String clientId, Integer retryTimes) {

        boolean isLock = distributionLockService.lock(activityCode, 500, clientId);

        if (retryTimes-- < 0) {
            return isLock;
        }
        if (!isLock) {
            isLock = tryLock(activityCode, clientId, retryTimes);
        }
        return isLock;
    }

    @GetMapping("setUp")
    public void setUp() {
        ActivityConfigVO activityConfigVO = new ActivityConfigVO();
        activityConfigVO.setActivityStartTime("2019-02-24 00:00:00");
        activityConfigVO.setActivityEndTime("2019-02-27 00:00:00");
        activityConfigVO.setRuleType("timeRange");
        activityConfigVO.setLotteryTimes(5);
        activityConfigVO.setLotteryStartDate("2019-02-25");
        activityConfigVO.setLotteryEndDate("2019-02-27");

        String str = JSONObject.toJSONString(activityConfigVO);
        stringRedisTemplate.opsForValue().set(KeyGenerator.getActivityConfig("fxh"), str);

        List<AwardsConfigVO> list = new ArrayList<>();

        for (int i = 0; i < 7; i++) {
            AwardsConfigVO awardsConfigVO = new AwardsConfigVO();
            awardsConfigVO.setAwardsName(i + "等奖");
            awardsConfigVO.setAwardsLevel(i + "");
            awardsConfigVO.setAwardsCode("100" + i);
            awardsConfigVO.setAwardsCounts(10);
            awardsConfigVO.setAwardsRate(new BigDecimal(0.12));
            list.add(awardsConfigVO);
        }
        AwardsConfigVO awardsConfigVO = new AwardsConfigVO();
        awardsConfigVO.setAwardsName("未中奖");
        awardsConfigVO.setAwardsLevel("-1");
        awardsConfigVO.setAwardsCode("8080");
        awardsConfigVO.setAwardsCounts(0);
        awardsConfigVO.setAwardsRate(new BigDecimal(0.16));
        list.add(awardsConfigVO);
        String arrStr = JSONArray.toJSONString(list);
        stringRedisTemplate.opsForValue().set(KeyGenerator.getAwardsConfig("fxh"), arrStr);

    }


    public static void main(String[] args) {

        /*Jedis jedis = null;
        try {
            jedis = new Jedis("127.0.0.1",6379);
            jedis.connect();

            String s = jedis.lindex("activity:list:winning:backup:123456788",0);

            AuldrawVo auldrawVo = JSONObject.parseObject(s, AuldrawVo.class);

            System.out.println(auldrawVo.getAwardsId());
        }catch (Exception e) {

        }finally {
            if(jedis != null) {
                jedis.close();
            }
        }*/

        Jedis jedis = null;

        try {
            jedis = new Jedis("127.0.0.1",6379);
            jedis.connect();

            String temp = jedis.get(KeyGenerator.getActivityQualiLotteryCounts("yy", "123456789"));

        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(jedis != null) {
                jedis.close();
            }
        }
    }
}